package references

import (
	"jvm/instructions/base"
	"jvm/rtda"
	"jvm/rtda/heap"
	. "jvm/typedef"
)

const (
	//Array Type  atype
	AT_BOOLEAN = 4
	AT_CHAR    = 5
	AT_FLOAT   = 6
	AT_DOUBLE  = 7
	AT_BYTE    = 8
	AT_SHORT   = 9
	AT_INT     = 10
	AT_LONG    = 11
)

// **************************************************
// 用于创建基本类型数组，有两个操作数，第一个是创建哪个类型的数组，
// 来自字节码，第二个是创建数组的长度，来自操作数栈。
// 根据数组类型获取到其数组类，并创建数组对象，将数组对象压入栈。
type NEW_ARRAY struct {
	atype U1
}

func (self *NEW_ARRAY) ReadOperands(reader *base.CodeReader) {
	self.atype = reader.ReadU1()
}

func (self *NEW_ARRAY) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	length := stack.PopInt()
	if length < 0 {
		panic("java.lang.NegativeArraySizeException")
	}
	arrayClass := getBasicArrayClass(frame.Method().Class().Loader(), self.atype)
	array := arrayClass.NewArray(U4(length))
	stack.PushRef(array)
}

// 获取基本类型数组类
func getBasicArrayClass(loader *heap.ClassLoader, atype U1) *heap.Class {
	switch atype {
	case AT_BOOLEAN:
		return loader.LoadClass("[Z")
	case AT_BYTE:
		return loader.LoadClass("[B")
	case AT_CHAR:
		return loader.LoadClass("[C")
	case AT_SHORT:
		return loader.LoadClass("[S")
	case AT_INT:
		return loader.LoadClass("[I")
	case AT_LONG:
		return loader.LoadClass("[J")
	case AT_FLOAT:
		return loader.LoadClass("[F")
	case AT_DOUBLE:
		return loader.LoadClass("[D")
	default:
		panic("Invalid atype!")
	}
}

// **************************************************
// 创建引用类型数组。有两个操作数，第一个是数组元素的类符号引用下标，
// 来自字节码，第二个是创建数组的长度，来自操作数栈。
// 根据数组元素类获取到其数组类，并创建数组对象，将数组对象压入栈。
type ANEW_ARRAY struct {
	base.U2IndexInst
}

func (self *ANEW_ARRAY) Execute(frame *rtda.Frame) {
	classRef := frame.Method().Class().ConstantPool().GetConstant(self.Index).(*heap.ClassRef)
	newClass := classRef.ResolvedClass()
	stack := frame.OPStack()
	length := stack.PopInt()
	if length < 0 {
		panic("java.lang.NegativeArraySizeException")
	}
	arrayClass := newClass.ArrayClass()
	array := arrayClass.NewArray(U4(length))
	stack.PushRef(array)
}

// **************************************************
// 创建多维数组
type MULTI_ANEW_ARRAY struct {
	index      U2
	dimensions U1
}

func (self *MULTI_ANEW_ARRAY) ReadOperands(reader *base.CodeReader) {
	self.index = reader.ReadU2()
	self.dimensions = reader.ReadU1()
}

func (self *MULTI_ANEW_ARRAY) Execute(frame *rtda.Frame) {
	classRef := frame.Method().Class().ConstantPool().GetConstant(self.index).(*heap.ClassRef)
	arrClass := classRef.ResolvedClass()
	if !arrClass.IsArray() {
		panic("not array")
	}
	stack := frame.OPStack()
	lengthes := popAndCheckCounts(stack, I4(self.dimensions))
	arr := newMultiArray(lengthes, arrClass)
	stack.PushRef(arr)
}
func popAndCheckCounts(stack *rtda.OPStack, dimensions I4) []I4 {
	lengthes := make([]I4, dimensions)
	for i := dimensions - 1; i >= 0; i-- {
		lengthes[i] = stack.PopInt()
		if lengthes[i] < 0 {
			panic("java.lang.NegativeArraySizeException")
		}
	}
	return lengthes
}
func newMultiArray(lengthes []I4, arrClass *heap.Class) *heap.Object{
	arr := arrClass.NewArray(U4(lengthes[0]))
	if len(lengthes) > 1 {
		refs := arr.Refs()
		for i, _ := range refs {
			refs[i] = newMultiArray(lengthes[1:], arrClass.ElementClass())
		}
	}
	return arr
}

// **************************************************
// 获取数组长度
type ARRAY_LENGTH struct {
	base.NoOperandsInst
}

func (self *ARRAY_LENGTH) Execute(frame *rtda.Frame) {
	stack := frame.OPStack()
	arrRef := stack.PopRef()
	if arrRef == nil {
		panic("java.lang.NullPointerException")
	}
	stack.PushInt(I4(arrRef.ArrayLength()))

}
